%define _PMM_EXTERN_OR_GLOBAL_ 1  
%include"../../include/pmm.mac"
%include"../../include/utils.mac"

;physical memory manager 
[section .text] 
;store memory infomation related addresses passed  
;from loader using eax,ebx,ecx
;input:
;	1.eax.Address of size;
;	2.ebx.Address of Map entries;
;	3.ecx.Address of map count. 
;outout:none 
store_memory_info_addr: 
	mov dword[memory_info_addr+memory_info_address.size_addr],eax  
	mov dword[memory_info_addr+memory_info_address.map_entries_addr],ebx
	mov dword[memory_info_addr+memory_info_address.map_count_addr],ecx  
	ret 

;set 1 block used 
;set bit in bitmap  
;input: 
;	1.index. index in the bitmap.  
;output:none 

;Used only for near call. 
;Stack frame: 
;|index	|  
;|eip	| 

;|ebp	| 
pmm_set_bit: 
	push ebp 
	mov ebp,esp 
	push ebx 
	push edx 
	push edi 
	pushfd  

	mov ebx,[ebp+8]
	;test index 
	cmp ebx,BM_BITS_NUM 
	jnbe .done  ;index >= bits number
	cmp ebx,0
	jl .done ;index < 0

	mov edi,bitmap 
	mov edx,ebx 
	;ebx=index/16
	;edx=index%16
	shr ebx,4
	and edx,0x000f

.move_to_word: ;move edi to word where index exist
	add edi,2
	sub ebx,1
	cmp ebx,0
	jg .move_to_word ;ebx > 0

	;bit test and set 
	;bitmap+2*ebx
	bts word[edi],dx
.done: 
	popfd  
	pop edi 
	pop edx 
	pop ebx 
	pop ebp 
	ret 8 

;set 1 block not used 
;clear bit in bitmap 
;input: 
;	1.index. index in the bitmap. 
;output:none 

;Used only for near call 
;Stack frame: 
;|index	|  
;|eip	| 

;|ebp| 
pmm_clear_bit:	
	push ebp 
	mov ebp,esp   
	push ebx 
	push ecx 
	push edi 
	pushfd  

	mov ebx,[ebp+8] ;get index
	;test input index
	cmp ebx,BM_BITS_NUM 
	jnbe .done ;index >= BM_BITS_NUM
	cmp ebx,0 ;index  < 0
	jl .done

	mov edi,bitmap 
	mov ecx,ebx 
	;ebx=index/16
	;ecx=index%16
	shr ebx,4
	and ecx,0x000f

.move_to_word: ;move to word where index exist
	add edi,2
	sub ebx,1
	cmp ebx,0
	jg .move_to_word

	;bit test and reset 
	btr word[edi],dx
.done:  
	popfd  
	pop edi 
	pop ecx 
	pop ebx 
	pop ebp  
	ret 8  ;clean up stack  
 
;set a region of memory used 
;input: 
;	1.base. base of region memory; 
;	2.length.length of region memory. 
;output:none 

;Used only for near call 
;Stack frame: 
;|base		|  
;|length	|  
;|eip		| 

;|ebp| 
pmm_set_region_use: 
	push ebp 
	mov ebp,esp ;stack frame  
	push ebx 
	pushfd  
	
	mov ebx,[ebp+12] ;base(first range)   
	mov edx,[ebp+8] ;length  
	add edx,ebx ;last range  

	;get first index and last index in bitmap 
	;base/4KB 
	;(base+length)/4KB 
	shr ebx,12 ;first index  
	shr edx,12 ;last index   
.set_bit_again:  
	push ebx 
	call pmm_set_bit   

	cmp ebx,edx ;to last index   
	jnbe .done  
	inc ebx 
	jmp .set_bit_again  

.done: 
	popfd  
	pop ebx  
	pop ebp  
	ret 12 ;clean up stack  

;set a region of memory not used 
;input: 
;	1.base. base of region memory; 
;	2.length. length of region memory.  
;output:none 

;Used only for near call 
;Stack frame: 
;|base		| 
;|length	| 
;|eip		| 

;|ebp		| 
pmm_set_region_unuse: 
	push ebp 
	mov ebp,esp ;stack frame pointer  
	push ebx 
	push edx 
	pushfd  

	mov ebx,[ebp+12] ;base 
	mov edx,[ebp+8] ;length  
	add edx,ebx ;base+length  

	;base/4KB,get first index in bitmap  
	;(base+length)/4KB,get last index in bitmap  
	shr ebx,12  
	shr edx,12 

.unset_loop: 
	push ebx 
	call pmm_clear_bit  

	cmp ebx,edx 
	jnbe .done    
	inc ebx
	jmp .unset_loop  

.done: 
	popfd 
	pop edx 
	pop ebx 
	pop ebp 
	ret 12 ;clear stack 

;initialize bitmap according to memory map entry 
;input:memory map entries  
;output:none 
pmm_init_bitmap: 
	push eax 
	push ebx  
	push esi 
	push edi 
	pushfd 

	mov esi,dword[memory_info_addr+memory_info_address.map_entries_addr];memory entries 
	mov edi,bitmap ;bitmap   
	xor ebx,ebx 

;test every entry 
.init_loop: 
	mov eax,dword[esi+8] ;length of map is zero?  
	xor eax,dword[esi+12] 
	jz .done 
	cmp dword[esi+16],1;type == 1? 
	jnz .init_loop ;loop again   
	push dword[esi] 
	push dword[esi+8] 
	call pmm_set_region_unuse 
	jmp .init_loop  ;loop again   

.done: 
	popfd 
	pop edi 
	pop esi 
	pop ebx  
	pop eax 
	ret 

;print memory map  
;input:address of memory map entries  
;output:none 

;Stack frame 
;|memory entries address| 
;|eip					| 

;Used in protect mode 
pmm_print_memory_map: 
	push ebp 
	mov ebp,esp 
	push eax 
	push edx 
	push edi 
	pushfd  

	mov esi,[ebp+8] ;get memory address  
	mov edx,0  ;y coordinate  
	call clear_screen_buffer ;clear screen  

.print_loop:  
	cmp dword[esi+8],0;length is 0?  
	jz .done   
	cmp dword[esi+16],1 ;type 1? 
	jz .entry_type_1  
	cmp dword[esi+16],2 ;type 2? 
	jz .entry_type_2 
	cmp dword[esi+16],3 ;type 3? 
	jz .entry_type_3  
	cmp dword[esi+16],4 ;type 4? 
	jz .entry_type_4   
	cmp dword[esi+16],5 ;type 5? 
	jz .entry_type_5    
	jmp .other_type  ;other type  

.entry_type_1: ;type 1  
	push mem_map_type_strs.type_1 
	push 0 
	push edx  
	call show_string_with_null  
	jmp .update_var  
	
.entry_type_2: ;type 2  
	push mem_map_type_strs.type_2 
	push 0 ;x  
	push edx ;y  
	call show_string_with_null  
	jmp .update_var  

.entry_type_3: ;type 3  
	push mem_map_type_strs.type_3  
	push 0 ;x  
	push edx ;y  
	call show_string_with_null 
	jmp .update_var  

.entry_type_4: ;type 4   
	push mem_map_type_strs.type_4 
	push 0 ;x  
	push edx ;y 
	call show_string_with_null  
	jmp .update_var  

.entry_type_5: ;type 5   
	push mem_map_type_strs.type_5  
	push 0 ;x  
	push edx ;y  
	call show_string_with_null  
	jmp .update_var  

.other_type: ;other type   
	push mem_map_type_strs.other_type 
	push 0 ;x 
	push edx ;y 
	call show_string_with_null  
	jmp .update_var  

.update_var: ;update variable   
	add esi,24 
	inc edx 
	jmp .print_loop  ;show again  
 
.done: 
	popfd   
	pop esi 
	pop edx  
	pop eax 
	pop ebp 
	ret 

;print memory size 
;input: 
;	1.address of memory size  
;output:none 

;Used only for near call 
;Stack frame 
;|memory size address	| 
;|eip					| 

;|ebp					| 
;Used in protect mode 
pmm_print_memory_size: 
	push ebp 
	mov ebp,esp 
	push eax 
	push edx 
	pushfd 

	mov eax,[ebp+8]
	mov dword[memory_size_str.hex],eax ;store memory size 
	
	CLEAR_REGS_MULTI_XOR eax,edx ;xor eax,eax;xor edx,edx
	mov al,byte[current_cursor_x]   
	mov dl,byte[current_cursor_y] 

	;show memory size 
	push memory_size_str  
	push eax 
	push edx 
	call show_string_with_null 

.done:  
	popfd 
	pop edx 
	pop eax 
	pop ebp 
	ret 4 

;print infomation about memory 
;input:none 
;output:none 
print_memory_info: 
	push dword[memory_info_addr+memory_info_address.size_addr]  
	call pmm_print_memory_size ;print memory size 

	push dword[memory_info_addr+memory_info_address.map_entries_addr]
	call pmm_print_memory_map ;print memory map  
	ret

;physical memory manager initialization
;input:none
;output:none
pmm_init: 
	call pmm_init_bitmap ;initialize bitmap 
	call print_memory_info ;print memory information 
	ret 

[section .data]
	;address of memory infomation get from boot time 
	;using BIOS routines 
	memory_info_addr:
		istruc memory_info_address
			at memory_info_address.size_addr,dw 0 
			at memory_info_address.map_entries_addr,dw 0 
			at memory_info_address.map_count_addr,dw 0 
		iend 
	
	;bit map 
	;bit 1 represents the mapping block is usable 
	;bit 0 represents the mapping block is unusable. 
	bitmap: 
		times BM_SIZE db 0xff ;at first,all block are used
	
	
	memory_size_str: ;string to strore memory size in hexidemal
		.prefix: db '0x' 
		.hex: times 10 db 0  

	;print memory map strings 
	mem_map_type_strs:  
		.type_1: db 'Usable(normal) RAM(Type 1)',0 
		.type_2: db 'Reserved--Unsable(Type 2)',0 
		.type_3: db 'ACPI reclaimable memory(Type 3)',0  
		.type_4: db 'ACPI NVS memory(Type 4)',0  
		.type_5: db 'Area containing bad memory(Type 5)',0   
		.other_type: db 'Other type',0  
